package com.haixiaoke.saas.common.weChat.util;

import com.haixiaoke.saas.common.utils.sign.Md5Utils;
import com.haixiaoke.saas.common.weChat.domain.ParseParam;
import org.apache.commons.lang3.StringUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.*;


/**
 * 微信工具类
 *
 * @author JsonChen
 */
public class WeChatUtils {
    public static final String FAIL = "FAIL";
    public static final String SUCCESS = "SUCCESS";
    private static final Logger log = LoggerFactory.getLogger(WeChatUtils.class);
    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final Random RANDOM = new SecureRandom();

    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }

    /**
     * 统一下单签名算法
     *
     * @param params 要参与签名的数据对象
     * @param mchKey 商户平台设置的密钥key
     * @return 签名
     */
    public static String getSign(SortedMap<String, Object> params, String mchKey) {
        StringBuffer sb = new StringBuffer();
        Set<Map.Entry<String, Object>> es = params.entrySet();
        Iterator<Map.Entry<String, Object>> it = es.iterator();
        while (it.hasNext()) {
            Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        // 商户平台密钥key必须放在最后面
        sb.append("key=" + mchKey);
        return Md5Utils.hash(sb.toString()).toUpperCase();
    }

    /**
     * 获取本机的IP
     *
     * @return 终端IP
     * @throws UnknownHostException
     */
    public static String getIp() throws UnknownHostException {
        // InetAddress.getLocalHost()方法获取本地主机的InetAddress对象，然后使用getHostAddress()方法获取IP地址
        InetAddress inetAddress = InetAddress.getLocalHost();
        return inetAddress.getHostAddress();
    }

    /**
     * 签名验证
     *
     * @param mchKey 商户平台设置的密钥key
     * @return
     */
    public static boolean signCheck(ParseParam parseParam, String mchKey) {
        // 微信返回数据中的签名
        String wxSign = parseParam.getSign();
        if (StringUtils.isBlank(wxSign)) {
            return false;
        }
        // 重新签名
        SortedMap<String, Object> paramMap = new TreeMap<String, Object>();
        paramMap.put("total_fee", parseParam.getTotalFee());
        paramMap.put("transaction_id", parseParam.getTransactionId());
        paramMap.put("out_trade_no", parseParam.getOutTradeNo());
        paramMap.put("attach", parseParam.getAttach());
        paramMap.put("return_code", parseParam.getReturnCode());
        paramMap.put("return_msg", parseParam.getReturnMsg());
        paramMap.put("result_code", parseParam.getResultCode());
        paramMap.put("err_code", parseParam.getErrCode());
        paramMap.put("err_code_des", parseParam.getErrCodeDes());
        paramMap.put("mch_id", parseParam.getMchId());
        paramMap.put("nonce_str", parseParam.getNonceStr());
        paramMap.put("time_end", parseParam.getTimeEnd());
        paramMap.put("sign", parseParam.getSign());
        paramMap.put("appid", parseParam.getAppId());
        paramMap.put("device_info", parseParam.getDeviceInfo());
        paramMap.put("sign_type", parseParam.getSignType());
        paramMap.put("openid", parseParam.getOpenId());
        paramMap.put("is_subscribe", parseParam.getIsSubscribe());
        paramMap.put("trade_type", parseParam.getTradeType());
        paramMap.put("bank_type", parseParam.getBankType());
        paramMap.put("settlement_total_fee", parseParam.getSettlementTotalFee());
        paramMap.put("fee_type", parseParam.getFeeType());
        paramMap.put("cash_fee", parseParam.getCashFee());
        paramMap.put("cash_fee_type", parseParam.getCashFeeType());
        paramMap.put("coupon_fee", parseParam.getCouponFee());
        paramMap.put("coupon_count", parseParam.getCouponCount());
        paramMap.put("coupon_type_$n", parseParam.getCouponType());
        paramMap.put("coupon_id_$n", parseParam.getCouponId());
        paramMap.put("coupon_fee_$n", parseParam.getCouponFeeTwo());
        String sign = getSign(paramMap, mchKey);

        // 二者签名对比验证
        return wxSign.equals(sign);
    }

    /**
     * 将xml格式字符串格式化对应Java对象
     *
     * @param xmlStr
     */
    public static ParseParam parseParam(String xmlStr) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(ParseParam.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        return (ParseParam) unmarshaller.unmarshal(new StringReader(xmlStr));
    }

    /**
     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点，则此节点的值是子节点的xml数据。
     *
     * @param strXml
     * @return
     * @throws JDOMException
     * @throws IOException
     */
    public static Map<String, String> doXMLParse(String strXml) throws JDOMException, IOException {
        strXml = strXml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");

        if (null == strXml || "".equals(strXml)) {
            return null;
        }
        Map m = new HashMap();
        InputStream in = new ByteArrayInputStream(strXml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }
            m.put(k, v);
        }
        // 关闭流
        in.close();

        return m;
    }

    /**
     * 获取子结点的xml
     *
     * @param children
     * @return String
     */
    public static String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if (!children.isEmpty()) {
            Iterator it = children.iterator();
            while (it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if (!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }
        return sb.toString();
    }

}
